/*
 * Copyright 2015-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.willowtreeapps.opentest4k

import kotlin.reflect.KClass
import kotlin.test.*

/**
 * Unit tests for [ValueWrapper].
 *
 * @author Marc Philipp
 * @author Sam Brannen
 * @since 1.0
 */
@Suppress("UNCHECKED_CAST")
class ValueWrapperTests {

    @Test
    fun wrapsNull() {
        val wrapper = null.toValueWrapper()

        assertNull(wrapper.valueType)
        assertNull(wrapper.value)
        assertEquals("null", wrapper.stringRepresentation)
        assertEquals(0, wrapper.identityHashCode)
        assertEquals("null", wrapper.toString())
        assertNull(wrapper.ephemeralValue)
    }

    @Test
    fun wrapsSerializableValue() {
        val wrapper = 1.2.toValueWrapper()

        assertEquals(Double::class, wrapper.valueType as KClass<Double>)
        assertEquals(1.2, wrapper.value)
        assertEquals("1.2", wrapper.stringRepresentation)
        assertNotEquals(0, wrapper.identityHashCode)
        val toString = wrapper.toString()
        assertTrue(toString.startsWith("1.2 (${javaOnlyString("java.lang.")}Double@"))
        assertTrue(toString.endsWith(")"))
        assertEquals(1.2, wrapper.ephemeralValue)
        assertSame(wrapper.value, wrapper.ephemeralValue)
    }

    @Test fun acceptsCustomStringRepresentation() {
        val wrapper: ValueWrapper = 1.2.toValueWrapper("1,20")
        assertEquals(Double::class, wrapper.valueType as KClass<Double>)
        assertEquals(1.2, wrapper.value)
        assertEquals("1,20", wrapper.stringRepresentation)
        assertNotEquals(0, wrapper.identityHashCode)
        val toString = wrapper.toString()
        assertTrue(toString.startsWith("1,20 (${javaOnlyString("java.lang.")}Double@"), toString)
        assertTrue(toString.endsWith(")"), toString)
    }

    @Test fun doesNotWrapAnotherValueWrapper() {
        val wrapper: ValueWrapper = 1.2.toValueWrapper()
        val same: ValueWrapper = wrapper.toValueWrapper()
        assertSame(wrapper, same)
    }

    @Test fun doesNotWrapAnotherValueWrapperWithSameCustomRepresentation() {
        val wrapper: ValueWrapper = 1.2.toValueWrapper("1,20")
        val same: ValueWrapper = wrapper.toValueWrapper("1,20")
        assertSame(wrapper, same)
    }

    @Test fun doesRepackageValueWrapperWithDifferentStringRepresentation() {
        val wrapper: ValueWrapper = 1.2.toValueWrapper()
        val same: ValueWrapper = wrapper.toValueWrapper("1,20")
        assertNotSame(wrapper, same)
    }

    @Test fun nullForCustomStringRepresentationFallsBackToDefaultToString() {
        val wrapper: ValueWrapper = 1.2.toValueWrapper(null)
        assertEquals(Double::class, wrapper.valueType as KClass<Double>)
        assertEquals(1.2, wrapper.value)
        assertEquals("1.2", wrapper.stringRepresentation)
        assertNotEquals(0, wrapper.identityHashCode)
        val toString = wrapper.toString()
        assertTrue(toString.startsWith("1.2 (${javaOnlyString("java.lang.")}Double@"), toString)
        assertTrue(toString.endsWith(")"), toString)
    }

    @Test
    fun wrapsNonSerializableValue() {
        class NonSerializable {

            override fun toString(): String {
                return "someString"
            }
        }

        val value = NonSerializable()

        val wrapper = value.toValueWrapper()

        onJava {
            assertNull(wrapper.value)
        }
        assertEquals("someString", wrapper.stringRepresentation)
        onJava {
            assertNotEquals(0, wrapper.identityHashCode)
        }

        val toString = wrapper.toString()
        assertTrue(toString) { toString.startsWith("someString (${javaOnlyString("com.willowtreeapps.opentest4k.ValueWrapperTests\$wrapsNonSerializableValue\$")}NonSerializable@") }
        assertTrue(toString) { toString.endsWith(")") }
    }

    @Test
    fun wrapsWithBrokenToString() {
        class BrokenToString : Serializable {
            override fun toString(): String {
                throw RuntimeException("toStringFailure")
            }
        }

        val value = BrokenToString()
        val wrapper = value.toValueWrapper()

        assertEquals(value, wrapper.value as BrokenToString)
        val representation = wrapper.stringRepresentation
        assertTrue(representation) { representation.contains("RuntimeException") }
        assertTrue(representation) { representation.contains("toStringFailure") }
        assertNotEquals(0, wrapper.identityHashCode)

        val toString = wrapper.toString()
        assertTrue(toString) { toString.contains("RuntimeException") }
        assertTrue(toString) { toString.contains("toStringFailure") }
    }
}

